<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div id="app">
    <input type="text" v-model="message">
    {{message}}
  </div>
</body>
<!-- <script src="./node_modules//vue//dist/vue.js"></script> -->
<!-- 响应式原理 -->
<script>
  class Vue {
    constructor(options) {
      // 1.保存数据
      this.$options = options;
      this.$data = options.data;
      this.$el = options.ed;

      // 2.将data添加到响应式系统
      new Observer(this.$data)

      // 3.代理this.$data的数据
      Object.keys(this.$data).forEach(key => {
        this._proxy(key)
      })
      // 4.处理el
      new Compiler(this.$el, this);//这里传了this是vue本身
    }
    // 对data中的数据做代理
    // 使用时可以直接 app.mesage 不需要app.$data.message
    _proxy(key) {
      Object.defineProperty(this, key, {
        configurable: true,
        enumerable: true,
        set(newValue) {
          this.$data[key] = newValue
        },
        get() {
          return this.$data[key]
        }
      })
    }
  }
  class Observer {
    constructor(data) {
      this.data = data;
      Object.keys(data).forEach(key => {
        this.defineReactive(this.data, key, data[key])
      })
    }
    defineReactive(data, key, val) {
      // 一个属性key 对应 一个 Dep对象
      const dep = new Dep();
      Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get() {
          if (Dep.target) {
            // 添加订阅者
            dep.addSub(Dep.target)//Dep.target = Watch
          }
          return val
        },
        set(newValue) {
          if (newValue === val) {
            return
          }
          val = newValue;
          dep.notify()
        }
      })
    }
  }
  class Dep {
    constructor() {
      this.subs = []
    }
    addSub(sub) {
      this.subs.push(sub)
    }
    notify() {
      this.subs.forEach(sub => {
        sub.update()
      })
    }
  }
  class Watch {
    constructor(node, name, vm) {
      this.node = node;
      this.name = name;
      this.vm = vm;
      Dep.target = this;
      this.update();
      //需要添加其他的监听，所以要清空
      Dep.target = null;
    }
    update() {
      this.node.nodeValue = this.vm[this.name]//这里一取this.name这个属性，就会调用defineProperty中的get方法
    }
  }
  const reg = /\{\{(.*)\}\}/;
  // . 匹配任何内容
  // * 0个或多个
  // + 1个或多个
  // {} 在正则中有特殊的含义
  class Compiler {
    constructor(el, vm) {//vm 指vue实例
      this.el = document.querySelector(el);
      this.vm = vm

      this.frag = this._createFragment();//创建片段
      this.el.appendChild(this.frag)
    }
    // 节点操作
    _createFragment() {
      //frag 片段对象
      const frag = document.createDocumentFragment()//创建了一虚拟的节点对象
      let child;
      while (child = this.el.firstChild) {
        this._compile(child);
        frag.appendChild(child);
      }
      return frag
    }
    // 双向绑定
    _compile(node) {
      if (node.nodeType === 1) {//标签节点
        const attrs = node.attributes
        if (attrs.hasOwnProperty('v-model')) {
          const name = attrs['v-model'].nodeValue
          node.addEventListener('input', e => {
            this.vm[name] = e.target.value;
          })
        }
      }
      if (node.nodeType === 3) {//文本节点
        console.log(reg.test(node.nodeValue));
        if (reg.test(node.nodeValue)) {
          const name = RegExp.$1.trim()
          console.log(name);
          new Watcher(node, name, this.vm)
        }
      }
    }
  }
</script>
<script>
  var app = new Vue({
    el: '#app',
    data: {
      message: '你好呀'
    }
  })
</script>

</html>